package com.ifox.hgx.spring.test;

/**
 * 拥有不同面值的货币，求，换一定面值的钱，用的货币的 数目 最少
 */
public class MakingChange {


    // 用于方法二中纪录求过的值，避免重复计算
    static int[] history = null;

    public static void main(String[] args) {

        int[] coinKinds = {1, 3, 5}; // 货币的种类，与 币值，此处共有三种货币，面值分别为 1，3，5

        int values = 12; // 需要兑换的面值

        // 调用方法 1
        System.out.println(method1(coinKinds, values));

        // 调用方法 2
        // method2(coinKinds,12);

        // 输出方法 2 求的的值
//        for (int i = 0; i < history.length; i++) {
//            System.out.print(" " + history[i]);
//        }

    }


    // 第一种方法，每种面值的依次计算，先计算面值 0 ，然后 1， 然后 2 ，然后3 。。。。 但是每种面值的计算依赖于前面的计算结果
    // temp1 = choices[ i - 面值种类[1] ] + 1
    // temp2 = choices[ i - 面值种类[2] ] + 1
    //            .......
    // 最后 choices[i] 中的值，为上述 temp 值中的最小值，此过程在程序中通过一个循环实现。
    //
    public static int method1(int[] coinKinds, int values) {

        values = values + 1;
        int[] choices = new int[values];   // 一维数组，choices[i] 表示的是 兑换 面值 i 可以有 choices[i] 种兑换方式

        // 求解具体每种面值的兑换方式 i 遍历的是从 0 开始到 values 的面值
        for (int i = 0; i < values; i++) {
            int mincoins = i; // 最基本的兑换方式就是，全部用 1 元的来兑，所以最多就是 mincoins 种方式
            // j 遍历的是不同币种的个数
            for (int j = 0; j < coinKinds.length; j++) {
                if (coinKinds[j] <= i) {
                    int temp = choices[i - coinKinds[j]] + 1;
                    if (mincoins > temp) {
                        mincoins = temp;
                    }
                }
            }
            choices[i] = mincoins;
        }
//        // 输出查看结果
//        System.out.println("面值:兑换方式");
//        for (int i = 0; i < choices.length; i++) {
//            System.out.println(" " + i + " :  " + choices[i]);
//        }
        return choices[choices.length-1] ;
    }

    // 第二种方法，采用递归的方法，history 中纪录的是每次求的的值
    // 避免重复进行递归计算
    public static void method2(int[] coinKinds, int values) {

        values = values + 1;
        history = new int[values];
        history[0] = 0;
        history[1] = 1;
        history[2] = 2;

        int mincoins = getMincoins(coinKinds, values);
        System.out.println(mincoins);
    }

    private static int getMincoins(int[] coinKinds, int value) {

        if (value <= 0) {
            return 0;
        }
        if (value == 1) {
            return 1;
        }
        if (value == 2) {
            return 2;
        }
        int mincoins = value;

        for (int coinKind = 0; coinKind < coinKinds.length; coinKind++) {
            // 找到前一个需要依赖的值
            int preCondition = value - coinKinds[coinKind];
            if (preCondition < 0) {
                break;
            }
            int temp;
            // 递归算法算过的值，都纪录到 history 中，避免重复计算
            if (history[preCondition] != 0) {
                temp = history[preCondition];
            } else {
                temp = getMincoins(coinKinds, preCondition);
            }
            if (mincoins > temp) {
                mincoins = temp;
            }
        }
        history[value] = mincoins + 1;
        return mincoins + 1;
    }

}
